package edu.vt.ece.rt.dstm;

import java.io.Serializable;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import edu.vt.ece.rt.dstm.contention.ContentionManager;
import edu.vt.ece.rt.quorum.ClustersManager;
import edu.vt.ece.rt.quorum.Resource;
import edu.vt.ece.rt.quorum.util.CreateDeleteSet;
import edu.vt.ece.rt.quorum.util.ReadWriteSet;
import edu.vt.ece.rt.util.Logger.Logger;

/**
 * All the STM implementations should implement this interface.
 * Extend this class to create different Implementations
 * TODO: Create a Interface Context 
 * @author	Sudhanshu
 */
public class Context implements Serializable, Comparable<Context>
{
	public static int aborts = 0;

	public AtomicInteger status;		//0 = Live, 10 = Aborted, 50 = committed
	protected Long txnId;

	private int lastRetries = 0;
	private int retries = 0;
	
	private ReadWriteSet rwS;
	private CreateDeleteSet cdS;

	public Object contentionMetadata;

	//To be used by Context delegator
	public Context(){
		rwS = new ReadWriteSet();
		cdS = new CreateDeleteSet();
		status = new AtomicInteger(0);
	}
	
	//Only to create a context on remote node, creator should register the transactionS
	public Context(Long tid){
		rwS = new ReadWriteSet();
		cdS = new CreateDeleteSet();
		status = new AtomicInteger(0);
		txnId = tid;
		retries++;		
	}
	
	//TODO: Merge to constructor if atomicBlockId no more required
	public void init(int atomicBlockId) {
//		HyFlow.getConflictManager().init(this);
		status.set(0);			//Set back to live
		
		txnId = System.currentTimeMillis() * 1000 + ClustersManager.baseClusterId;// + (Thread.currentThread().getId()%100)*10;
		retries++;
		
		//Clear the read write and create delete sets.
		rwS.Clear();
		cdS.Clear();
		register();
		ContentionManager.initContext(this);
	}
	
	public void register(){
		//Register context
		Logger.debug("Registered "+txnId+" with status "+status.get());
		ContextDelegator.contextMap.put(this.txnId, this);
	}
	
	public int getRetries(){
		return lastRetries;
	}
	
	public void complete() {
		lastRetries = retries;
		retries = 0;
//		HyFlow.getConflictManager().complete(this);
	}

	public Long getContextId(){
		return txnId;
	}
	
	@Override
	public String toString() {
		return "[" + txnId + "|" + status + "]";
	}
	
	public void addToReadSet(QObject obj){
		rwS.addReadObject(obj);
	}
	
	public void addToWriteSet(QObject obj){
		rwS.addWriteObject(obj);
	}
	
	public void addToCreateSet(QObject obj){
		cdS.addCreateObject(obj);
	}
	
	public void addToDeleteSet(QObject obj){
		cdS.addDeleteObject(obj);
	}
	
	public Set<QObject> getReadSet(){
		return rwS.getReadObjects();
	}
	
	public Set<QObject> getWriteSet(){
		return rwS.getWriteObjects();
	}
	
	public Set<QObject> getCreateSet(){
		return cdS.getCreateObjects();
	}
	
	public Set<QObject> getDeleteSet(){
		return cdS.getDeleteObjects();
	}
	
	public void setAborted(){
		Logger.debug("Trying to abort "+txnId + " current status "+status.get());
		status.compareAndSet(0, 10);
	}
	
	public void setCommited(){
		Logger.debug("Trying to commit "+txnId + " current status "+status.get());
		status.compareAndSet(0, 50);
	}

	/**
	 * @return
	 */
	public boolean isCommitted() {
		return status.get() == 50;
	} 
	
	public boolean isAborted(){
		return status.get()==10;
	}
	/* (non-Javadoc)
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	@Override
	public int compareTo(Context c) {
		return ContentionManager.resolve(this, c);
	}

	/**
	 * Publishes the Create and Delete set objects locally
	 */
	public void publishLocally() {
		for(QObject obj: getCreateSet()){
			Resource.putQObject(obj.getId(), obj);
		}
		
		for(QObject obj: getCreateSet()){
			Resource.removeObject(obj);
		}
	}

	/**
	 * @return
	 */
	public int getStatus() {
		return status.get();
	}
}
